package com.demo.kotlin.network

import android.net.ParseException
import com.demo.kotlin.BuildConfig
import com.demo.kotlin.bean.base.BaseBean
import com.google.gson.Gson
import com.google.gson.JsonParseException
import com.puhui.lib.network.callback.NetworkCallBack
import com.puhui.lib.utils.LogUtils
import com.puhui.lib.utils.ToastUtil
import com.puhui.lib.utils.reportCaughtException
import org.json.JSONException
import retrofit2.HttpException
import java.io.IOException
import java.net.SocketException
import java.net.SocketTimeoutException
import java.net.UnknownHostException

abstract class BaseNetworkCallBack<T : BaseBean> : NetworkCallBack<T> {

    companion object {
        //结合RequestInterceptor: BaseNetworkCallBack.errorCode = code，处理Null is not a valid element的问题
        var errorCode = "200"
    }

    override fun onResponse(response: T?) {
        response?.let {
            if (it.code == CommonApiService.SUCCESS_FLAG) {
                onSuccess(it)
            } else {
                it.message?.let { msg -> onErrorWithCode(it.code, msg) }
            }

            onComplete()
            return
        }

        onError(Throwable("请求成功，但数据异常"), "接口返回数据为null")
    }

    /**
     * 处理后台返回的错误信息，如下情况（Http的code却也是402的情况）：
     *  { "code": 402, "message": "The Mobile is required"  }
     *  '203'=>'', // 授权失败|用户无权限    '205'=>'', // 用户未登录
     */
    open fun onErrorWithCode(code: String, msg: String) {
        if ("203" == code || "205" == code) {
            doUnLoginStatus()
        } else {
            onError(Throwable(msg), msg)
        }
    }

    @Synchronized
    open fun doUnLoginStatus() {
        //@Synchronized + SpUtilsKt解决多个接口同时报token失效情况下，导致登录页面多次打开问题
//        if (!SpUtilsKt.getBoolean(LoginActivity.IsLoginActivityVisible)) {
//            UserInfoDbUtils.loginOut()
//            ActivityUtils.finishToActivity(MainActivity::class.java, false)
//            ActivityUtils.startActivity(LoginActivity::class.java)
//            EventBus.getDefault().post(LoginChangedEvent(false))
//            SpUtilsKt.put(LoginActivity.IsLoginActivityVisible, true)
////            ToastUtil.toastShort("Token invalid")
//        }
    }

    override fun onFailure(throwable: Throwable) {
        var msg = throwable.localizedMessage ?: "network busy" //网络繁忙
        if (throwable is UnknownHostException || throwable is SocketException) {
            msg = "network unusable"  //网络不可用
            onNetworkError(msg)
        } else if (throwable is SocketTimeoutException) {
            msg = "network connect timeout"  //请求网络超时
            onNetworkError(msg)
        } else if (throwable is NullPointerException) {
            // https://www.it610.com/article/1281673515799429120.htm
            // RxJava+Retrofit的一个bug,就是如果后台只返回一个响应码，而不返回body的情况下，
            // 会出现java.lang.NullPointerException: Null is not a valid element的bug, 看了一下官网，好像是Rxjava2的onNext()需要返回一个body
            if (throwable.message?.contains("Null is not a valid element") == true) {
                onErrorWithCode(errorCode, throwable.message ?: "")
                onComplete()
                return
            }
        } else if (throwable is HttpException) {
            val errorBody = throwable.response()?.errorBody()
            errorBody?.let {
                try {
                    // PHP 后台将错误状态码与Http的状态码混合在一起处理的，故这里从errorBody中取错误内容
                    val errorInfo = Gson().fromJson(it.string(), BaseBean::class.java)
                    if (null != errorInfo) {
                        msg = errorInfo.message ?: if (BuildConfig.DEBUG) "接口错误描述为空" else ""
                        onErrorWithCode(errorInfo.code, msg)
                        onComplete()
                        return
                    } else {
                        msg = convertStatusCode(throwable)
                        reportCaughtException(throwable)
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                    if (BuildConfig.DEBUG) LogUtils.printE("errorBody 解析出问题")
                }
            }
        } else if (throwable is JsonParseException || throwable is ParseException || throwable is JSONException) {
            if (BuildConfig.DEBUG) msg = "数据解析错误"
            reportCaughtException(throwable)
        } else if (throwable is IOException && msg == "Canceled") {
            if (BuildConfig.DEBUG) LogUtils.printE("Context销毁，请求被取消")
            return
        }

        if (BuildConfig.DEBUG) throwable.printStackTrace()
        onError(throwable, msg)
        onComplete()
    }

    open fun onNetworkError(msg: String) {
        ToastUtil.showToast(msg)
    }

    abstract fun onSuccess(body: T)

    open fun onError(throwable: Throwable, msg: String) {
        ToastUtil.showToast(msg)
    }

    open fun onComplete() {}

    private fun convertStatusCode(httpException: HttpException): String {
        val msg = when {
            httpException.code() == 500 -> "服务器发生错误"
            httpException.code() == 404 -> "请求地址不存在"
            httpException.code() == 403 -> "请求被服务器拒绝"
            httpException.code() == 307 -> "请求被重定向到其他页面"
            else -> "网络异常"
        }
        return msg + httpException.code()
    }
}